package cinvestav.adviseme.databasemodel;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import cinvestav.adviseme.datamodel.Lugar;
import cinvestav.adviseme.datamodel.Persona;
import cinvestav.adviseme.datamodel.TipoLugar;
import cinvestav.adviseme.util.DBException;
import cinvestav.adviseme.util.DataBaseDictionary;

public class LugarDB extends DataBase implements ILugarDB {

	private static Logger log = Logger.getLogger(LugarDB.class.getName());

	/**
	 * Constructor vacio
	 * 
	 * @throws DBException
	 */
	public LugarDB() throws DBException {
		super();
		// TODO Auto-generated constructor stub
	}

	@Override
	public List<Lugar> getAll() throws DBException {
		// TODO Auto-generated method stub
		try {
			Connection c = this.getConnection();
			ResultSet res = this.ejecutarQuery(c
					.prepareStatement(DataBaseDictionary.GET_ALL_LUGARES));
			List<Lugar> lugares = new LinkedList<Lugar>();
			while (res.next()) {
				Lugar lugar = new Lugar();
				TipoLugar tipoLugar = new TipoLugar();
				lugar.setId(res.getInt("ide_lug"));
				lugar.setNombre(res.getString("nom_lug"));
				lugar.setDescripcion(res.getString("des_lug"));
				lugar.setLatitud(res.getDouble("lat_lug"));
				lugar.setLongitud(res.getDouble("lon_lug"));
				lugar.setRanking(res.getInt("ran_lug"));
				lugar.setUrlPrincipal(res.getString("fot_lug"));
				tipoLugar.setId(res.getInt("ide_tip"));
				tipoLugar.setDescripcion(res.getString("des_tip"));
				lugar.setTipo(tipoLugar);
				lugares.add(lugar);
			}
			c.close();
			return lugares;
		} catch (SQLException ex) {
			throw new DBException("Error al ejecutar la consulta:\n"
					+ DataBaseDictionary.GET_ALL_LUGARES);
		}
	}

	@Override
	public List<Lugar> getByFiter(Map<String, String[]> filtros)
			throws DBException {

		try {
			// Se inicializa la coneccion
			// Se obtiene una nueva conexion
			Connection c = this.getConnection();
			// Se realiza la consulta
			ResultSet res = this.ejecutarQuery(genPreparedStatementString(
					filtros, c));
			// Procesamiento de resultados
			List<Lugar> lugares = new LinkedList<Lugar>();
			// Por cada elemento en el resultSet, se genera una nueva linea y se
			// agrega a la lista
			while (res.next()) {
				// Los elementos del resultSet se obtienen a partir del nombre
				// de la columna
				// O por su indice en la tabla
				Lugar lugar = new Lugar();
				TipoLugar tipoLugar = new TipoLugar();
				lugar.setId(res.getInt(DataBaseDictionary.ALIAS_LUGAR
						+ ".ide_lug"));
				lugar.setNombre(res.getString(DataBaseDictionary.ALIAS_LUGAR
						+ ".nom_lug"));
				lugar.setDescripcion(res
						.getString(DataBaseDictionary.ALIAS_LUGAR + ".des_lug"));
				lugar.setLatitud(res.getDouble(DataBaseDictionary.ALIAS_LUGAR
						+ ".lat_lug"));
				lugar.setLongitud(res.getDouble(DataBaseDictionary.ALIAS_LUGAR
						+ ".lon_lug"));
				lugar.setRanking(res.getInt(DataBaseDictionary.ALIAS_LUGAR
						+ ".ran_lug"));
				lugar.setUrlPrincipal(res
						.getString(DataBaseDictionary.ALIAS_LUGAR + ".fot_lug"));
				tipoLugar.setId(res.getInt(DataBaseDictionary.ALIAS_TIPOLUGAR
						+ ".ide_tip"));
				tipoLugar.setDescripcion(res
						.getString(DataBaseDictionary.ALIAS_TIPOLUGAR
								+ ".des_tip"));
				lugar.setTipo(tipoLugar);
				lugares.add(lugar);
			}
			c.close();
			return lugares;
		} catch (SQLException ex) {
			log.log(Level.SEVERE, null, ex);
			throw new DBException("Error al ejecutar la consulta:\n"
					+ ex.getMessage());
		} catch (DBException ex) {
			throw new DBException(ex);
		}
	}

	@Override
	public Boolean delete(Lugar lugar) throws DBException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Boolean insert(Lugar lugar) throws DBException {
		// TODO Auto-generated method stub
		try {

			// Se inicializa la coneccion
			// Se obtiene una nueva conexion
			Connection c = this.getConnection();
			// Si no, se procede a insertar la solicitud
			PreparedStatement ps = c
					.prepareStatement(DataBaseDictionary.INSERT_LUGAR);
			/*
			 * Se insertan los parametros al insert
			 * [1] = Nombre del Lugar
			 * [2] = Descripcion 
			 * [3] = latitud 
			 * [4] = longitud 
			 * [5] = Tipo (ID)
			 */

			ps.setString(1, lugar.getNombre());
			ps.setString(2, lugar.getDescripcion());
			ps.setDouble(3, lugar.getLatitud());
			ps.setDouble(4, lugar.getLongitud());
			ps.setInt(5, lugar.getTipo().getId());

			// Se realiza la consulta
			this.ejecutarInstruccion(ps);
			// Procesamiento de resultados
			c.close();
			return true;
		} catch (SQLException ex) {
			log.log(Level.SEVERE, null, ex);
			throw new DBException("Error al ejecutar la consulta:\n"
					+ ex.getMessage(), ex);
		}
	}

	@Override
	public List<Lugar> getFriends(Lugar lugar) throws DBException {
		// TODO Auto-generated method stub
		return null;
	}

	/**
	 * Genera un PreparedStatement apartir de un Mapa de filtros y una coneccion
	 * 
	 * @param filtros
	 *            Mapa de filtros que se utilizaran para generar la consulta
	 * @param c
	 *            Conexion que ejecutara el PreparedStatement
	 * @return Un preparedStatement listo para ser ejecutado
	 * @throws SQLException
	 */
	private PreparedStatement genPreparedStatementString(
			Map<String, String[]> filtros, Connection c) throws SQLException {

		Boolean ini = Boolean.TRUE;
		Integer valID = 0;
		// Se obtiene la consulta base
		// A esta cadena se le iran concatenando los filtros segun corresponda
		String statement;
		if (filtros
				.containsKey(DataBaseDictionary.COLUMN_FILTER_NAME_PERSONA_ID)) {
			String[] val = filtros
					.remove(DataBaseDictionary.COLUMN_FILTER_NAME_PERSONA_ID);
			valID = Integer.parseInt(val[0]);
			statement = DataBaseDictionary.GET_BASE_LUGAR_SUGERIDO;
			ini = Boolean.FALSE;
		} else {
			statement = DataBaseDictionary.GET_BASE_LUGAR;
		}

		// SE obtiene el alias de la tabla a utilizar
		String alias = DataBaseDictionary.ALIAS_LUGAR;
		// Se recorre el mapa de filtros para generar el String de la consulta
		for (Map.Entry<String, String[]> entry : filtros.entrySet()) {
			// if
			// (entry.getKey().compareTo(DataBaseDictionary.COLUMN_FILTER_NAME_PERSONA_ID)!=0){
			// entry - Contiene la entrada del mapa una por una
			// Si NO es el primer elemento se agrega un AND a la consulla
			if (!ini) {
				statement += " AND";
			} else {
				// Si es el primer filtro, se agrega la clausula WHERE a la
				// consulta
				ini = Boolean.FALSE;
				statement += " WHERE ";
			}
			// Se agrega el filtro definido por el mapa
			statement += " " + entry.getValue()[2] + "." + entry.getKey();
			// }
		}
		Logger.getLogger(LugarDB.class.getName()).log(Level.INFO,
				"SQL = " + statement);
		// Una vez generada la cadena, se llenaran los datos del
		// PreparedStatement '?'

		// Se inicializa el PreparedStatement y se liga este a la coneccion
		PreparedStatement ps = c.prepareStatement(statement);
		// llevara el indice de los elementos del ps, debe iniciar en 1

		int cont = 1;
		if (valID != 0) {
			ps.setInt(cont, valID);
			cont = 2;
			ps.setInt(cont, valID);
			cont = 3;
		}
		// Se recorre nuevamente el mapa de filtros
		for (Map.Entry<String, String[]> entry : filtros.entrySet()) {
			String[] val = entry.getValue();
			// Dependiendo del tipo de filtro, se agregan los valores al ps
			if (val[1].compareTo(DataBaseDictionary.STRING_SIMPLE) == 0) {
				// Si es de tipo 'String_SIMPLE' es decir la busqueda se realiza
				// con '='
				ps.setString(cont, val[0]);
			} else if (val[1].compareTo(DataBaseDictionary.STRING_JOKER) == 0) {
				// En este caso la busqueda se realiza con LIKE y se agrega el
				// comodin de busqueda '%' al final del valor a buscar
				ps.setString(cont, val[0] + "%");
			} else if (val[1].compareTo(DataBaseDictionary.STRING_DOBLE_JOKER) == 0) {
				// En este caso la busqueda se realiza con LIKE y se agrega el
				// comodin de busqueda '%' al final y al inicio del valor a
				// buscar
				ps.setString(cont, "%" + val[0] + "%");
			} else if (val[1].compareTo(DataBaseDictionary.INTEGER_TYPE) == 0) {
				// Cuando el valor es entero
				ps.setInt(cont, Integer.parseInt(val[0]));
			}
			cont++;
		}
		return ps;
	}

}
